%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%----------------------------------------------------------------------
%% Purpose: This is a simple logger utility for the HDLT toolkit.
%%          It assumesd that the debug level and the "name" of the 
%%          logging entity has been put in process environment
%%          (using the set_level and set_name functions respectively).
%%----------------------------------------------------------------------

%%

-module(hdlt_logger).

-include_lib("kernel/include/file.hrl").
-include("common.hrl").

-behaviour(gen_server).
-export([start_link/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-record(state, {monitorRef, logCnt}).



-export([
	error_out/2, warn/2, log/2, debug/2
]).
-export([deleteHandler/0]).



-define(LOGGER, ?MODULE).
-define(MSG, hdlt_logger_msg).
-define(NAME, hdlt_logger_name).
-define(ERROR_STR, "ERROR").
-define(WARN_STR, "WARN").
-define(LOG_STR, "LOG ").
-define(DEBUG_STR, "DBG ").
-define(MAX_LOG_CNT_ONE_FILE, 500000).    %%日志文件最大行数
-define(Start_Link_TimeOut_ms, 120000).

%%打印等级
-define(SILENCE_LEVEL, 0).
-define(ERROR_LEVEL, 1).
-define(WARN_LEVEL, 2).
-define(INFO_LEVEL, 3).
-define(DEBUG_LEVEL, 4).





start_link(Log_File_Name) ->
	ParentPid = self(),
	gen_server:start_link({local, ?LOGGER}, ?MODULE, [Log_File_Name, ParentPid], [{timeout, ?Start_Link_TimeOut_ms}]).



init([Log_File_Name, ParentPid]) ->
	erlang:system_flag(backtrace_depth, 5), %%最大栈回溯层数设置为5层，默认是8层太多
	Ref = initLogger(Log_File_Name, ParentPid),
	{ok, #state{monitorRef = Ref, logCnt = 0}}.


handle_call(_Request, _From, State) ->
	{noreply, ok, State}.

handle_cast(_Msg, State) ->
	{noreply, State}.



terminate(_Reason, _State) ->
	ok.

code_change(_OldVsn, State, _Extra) ->
	{ok, State}.

error_out(F, A) ->
	CurLogLevel = getLogLevel(),
	if
		CurLogLevel >= ?ERROR_LEVEL ->
			do_log(?ERROR_LEVEL, F, A);
		?TRUE -> ok
	end.

warn(F, A) ->
	CurLogLevel = getLogLevel(),
	if
		CurLogLevel >= ?WARN_LEVEL ->
			do_log(?WARN_LEVEL, F, A);
		?TRUE -> ok
	end.

log(F, A) ->
	CurLogLevel = getLogLevel(),
	if
		CurLogLevel >= ?INFO_LEVEL ->
			do_log(?INFO_LEVEL, F, A);
		?TRUE -> ok
	end.

debug(F, A) ->
	CurLogLevel = getLogLevel(),
	if
		CurLogLevel >= ?DEBUG_LEVEL ->
			do_log(?DEBUG_LEVEL, F, A);
		?TRUE -> ok
	end.

deleteHandler() ->
	?LOGGER ! {deleteHandler}.

initLogger(Log_File_Name, Parent) ->
	put(?NAME, Log_File_Name),
	Ref = erlang:monitor(process, Parent),
	make_log_file(Log_File_Name),
	trunk_at_next_day(),
	Ref.


handle_info(Info, #state{monitorRef = Ref, logCnt = Cnt} = StateData) ->
	try
		case Info of
			{?MSG, Level, F, A} ->
				LogFileHandle = case Level of
					                ?ERROR_LEVEL ->
						                get('Log_File_Handle_For_Err');
									?WARN_LEVEL ->
										get('Log_File_Handle_For_Err');
					                ?INFO_LEVEL ->
						                get('Log_File_Handle');
					                ?DEBUG_LEVEL ->
						                get('Log_File_Handle')
				                end,
				try
					case isWindowsOS() andalso Level == ?ERROR_LEVEL of
						true ->
							io:format(F, A);
						false -> ok
					end,

					case is_pid(LogFileHandle) of
						?TRUE -> io:format(LogFileHandle, F, A);
						_ ->
							make_log_file(get(?NAME)),
							self() ! Info
					end
				catch
					Throw ->
						io:format(LogFileHandle, "~p", [Throw])
				end,

				Cnt1 = Cnt + 1,
				case Cnt1 > ?MAX_LOG_CNT_ONE_FILE of
					true ->
						Cnt2 = 0,
						make_log_file(get(?NAME));
					false ->
						Cnt2 = Cnt1
				end,
				{noreply, #state{monitorRef = Ref, logCnt = Cnt2}};
			{'DOWN', Ref, process, _Object, _Info} ->
				%% start the stop timer
				erlang:send_after(timer:seconds(5), self(), stop),
				{noreply, StateData};
			stop ->
				{stop, normal, StateData};
			{deleteHandler} ->
				catch file:close(get('Log_File_Handle_For_Err')),
				erase('Log_File_Handle_For_Err'),
				catch file:close(get('Log_File_Handle')),
				erase('Log_File_Handle'),
				{noreply, StateData};
			trunk_file ->
				make_log_file(get(?NAME)),
				trunk_at_next_day(),
				{noreply, StateData};
			Unkown ->
				io:format("hdlt_looger recv unkown msg:~p ~n", [Unkown]),
				{noreply, StateData}
		end

	catch
		_:_Why:StackTrace ->
			error_out("ExceptionFunc_Module:[~p] ExceptionFunc[hande_info] Why[~p] ~n stack[~p] ~n Info ~p",
				[?MODULE, _Why, StackTrace, Info]),
			{noreply, StateData}
	end.


formated_timestamp() ->
	{Date, Time} = erlang:localtime(),
	{YYYY, MM, DD} = Date,
	{Hour, Min, Sec} = Time,
	FormatDate =
		io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w",
			[YYYY, MM, DD, Hour, Min, Sec]),
	lists:flatten(FormatDate).

do_log(Level, F, A) ->
	{SEV, A1} = case Level of
		            ?ERROR_LEVEL ->
			            {?ERROR_STR, A};
		            ?WARN_LEVEL ->
			            {?WARN_STR, A};
		            ?INFO_LEVEL ->
			            {?LOG_STR, A};
		            ?DEBUG_LEVEL ->
			            {?DEBUG_STR, A};
		            _ -> {"", ""}
	            end,
	RoleID = case catch role_data:getRoleID() of
				 ID when is_integer(ID) -> ID;
				 _ -> 0
	         end,
	Msg = {?MSG, Level, "[~s] [~p] [~p] [roleid:~p] ->" ++ F ++ "\r\n", [formated_timestamp(), SEV, self(),RoleID | A1]},
	(catch ?LOGGER ! Msg).

make_log_file(Log_File_Name) ->
	case get('Log_File_Handle') of
		0 -> ok;
		undefined -> ok;
		Handle1 -> file:close(Handle1)
	end,

	case get('Log_File_Handle_For_Err') of
		0 -> ok;
		undefined -> ok;
		Handle2 -> file:close(Handle2)
	end,


	{ok, Cur_Dir} = file:get_cwd(),
	Log_Dir = Cur_Dir ++ "/log",

	case file:read_file_info(Log_Dir) of
		{ok, FileInfo} when FileInfo#file_info.type == directory ->
			ok;
		{error, _Reason} ->
			case file:make_dir(Log_Dir) of
				ok ->
					ok;
				{error, Reason} ->
					io:format("create log_dir[~s] fail[~w].~n", [Log_Dir, Reason])
			end
	end,

	{Date, _Time} = erlang:localtime(),
	{YYYY, MM, DD} = Date,
	Time_Format = io_lib:format("~.4w-~.2.0w-~.2.0w",
		[YYYY, MM, DD]),
	Log_Full_FileName = checkDupName(Log_Dir ++ "/" ++ Log_File_Name ++ "-" ++ lists:flatten(Time_Format)),
	Log_Full_FileName_For_Err = checkDupName(Log_Dir ++ "/err-" ++ Log_File_Name ++ "-" ++ lists:flatten(Time_Format)),

	{Result, File_Handle} = file:open(Log_Full_FileName, [append]),
	case Result of
		ok ->
			put('Log_File_Handle', File_Handle),
			ok;
		error ->
			io:format("create log_file[~s] fail[~w].~n", [Log_Full_FileName, File_Handle])
	end,

	{Result1, File_Handle_ForError} = file:open(Log_Full_FileName_For_Err, [append]),
	case Result1 of
		ok ->
			put('Log_File_Handle_For_Err', File_Handle_ForError),
			ok;
		error ->
			io:format("create err_log_file[~s] fail[~w].~n", [Log_Full_FileName_For_Err, File_Handle_ForError])
	end.

isWindowsOS() ->
	element(1, os:type()) =:= win32.

getLogLevel() ->
	case data_setting:get(log_level) of
		?UNDEFINED -> ?DEBUG_LEVEL;
		Level -> Level
	end.

%%检测重复的名字，并返回新名字
checkDupName(Name) ->
	case filelib:is_file(Name++".log") of
		?TRUE ->
			checkDupName_1(Name,1);
		_ -> Name++".log"
	end.
checkDupName_1(Name,100) ->%%默认最多检测100次
	Name++".log";
checkDupName_1(Name,Count) ->
	Name1 = Name++"_"++integer_to_list(Count)++".log",
	case filelib:is_file(Name1) of
		?TRUE ->
			checkDupName_1(Name,Count+1);
		_ -> Name1
	end.

%%通知服务器在下一个整点刷新日志文件
trunk_at_next_day() ->
	{_, {H, M, S}} = erlang:localtime(),
	Time = ((23 - H) * 3600 + (59 - M) * 60 + (59 - S) + 2) * 1000,
	erlang:send_after(Time, self(), trunk_file).